require 'net/http'
require 'rexml/document'
require 'rexml/formatters/transitive'
require 'set'

task :collect_hudson_plugins_list do
  wondercap.filter_names
  
  result = filtered_configurations.inject(Set.new) { |memo,conf| memo.merge(conf.hudson_plugins) }
  ui.say(ui.list(result.map { |name| name.to_s }.to_a))
end

extension :build => :copy_to_hudson_workspace do
  task :build do
    execute_previous

    next unless ENV["JOB_NAME"] == configuration_name.to_s    
    hudson_workspace_path = ENV["WORKSPACE"]
    unless hudson_workspace_path
      logger.debug "can't find environment variable WORKSPACE that should be set when doing hudson build"
      next
    end

    dest_path = File.join hudson_workspace_path

    cur_path_lstat = File.lstat Dir.pwd
    dest_path_lstat = File.lstat dest_path
    
    # NOTE: this is required because during Hudson builds we can delete our working directly which will lead to bad consequences
    chdir_needed = (cur_path_lstat.ino == dest_path_lstat.ino && cur_path_lstat.dev == dest_path_lstat.dev)
    
    Dir.chdir Dir.tmpdir if chdir_needed
    FileUtils.rm_r dest_path, :force => true, :secure => true
    FileUtils.mkdir_p dest_path
    Dir.chdir dest_path if chdir_needed

    FileUtils.cp_r File.join(revision_build.patched_src_path, "."), dest_path
    FileUtils.cp_r revision_build.dist_path, File.join(dest_path, "dist")
  end
end

extension :hudson => :base do
  set :hudson_plugins, Set.new
    
  task :fill_hudson_plugins, :on_first_fetch => :hudson_plugins do
  end
end

# produce_hudson_configuration-related extensions and hooks
configuration :base do
  use :hudson => :base
  
  before :produce_hudson_configuration do
    class << self; self; end.class_eval do
      def hudson_job(&block)
        @hudson_job ||= ::Wondercap::Extensions::Hudson::XmlConfigurator.new(self, REXML::Document.new << REXML::XMLDecl.new(REXML::XMLDecl::DEFAULT_VERSION, "utf-8"))
        @hudson_job.instance_eval(&block) if block
        @hudson_job
      end
    end
  end
  
  after :produce_hudson_configuration do
    hudson_job {
      project {
        actions {}
        description("")
        logRotator {
          daysToKeep(fetch(:hudson_days_to_keep, 42))
          numToKeep(-1)
          artifactDaysToKeep(-1)
          artifactNumToKeep(-1)
        }
        keepDependencies(false)
        properties {}

        canRoam(true)
        disabled(false)

        triggers {}
        builders {}
        publishers {}
        buildWrappers {}
      }
    }
    
    doc = hudson_job.root

    conf_path = File.join(fetch(:hudson_root), "jobs", configuration_name.to_s, "config.xml")
    FileUtils.mkdir_p File.dirname(conf_path)
    if File.exists? conf_path
      FileUtils.cp conf_path, "#{conf_path}.bak"
    end
    
    open conf_path, "w" do |fout|
        formatter = REXML::Formatters::Transitive.new
        formatter.write doc, fout
    end  
  end
end

extension :hudson => :quiet_period do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        quietPeriod(fetch(:hudson_quiet_period))
      }
    }
  end
end

extension :hudson_trigger => :remote_job_trigger do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    if exists? :hudson_remote_build_token
      hudson_job {
        project {
          authToken(fetch(:hudson_remote_build_token))
        }
      }
    end
  end
  
  task :start_hudson_job do
    job_url = "#{fetch(:hudson_master_url)}/job/#{fetch(:hudson_job_name, configuration_name)}/build"
    token = fetch(:hudson_remote_build_token, nil)
    job_url += "?token=#{token}" if token
    logger.debug "using url: #{job_url}"
    
    uri = URI.parse(job_url)
    response = Net::HTTP.start(uri.host, uri.port) do |http|
      req = Net::HTTP::Get.new(uri.path)
      req.basic_auth fetch(:hudson_basic_auth_user), fetch(:hudson_basic_auth_password) if exists?(:hudson_basic_auth_user) && exists?(:hudson_basic_auth_password)
      req.use_ssl = true if uri.scheme == "https"
      http.request(req)
    end
    logger.debug "hudson response: #{response}"
    case response
    when Net::HTTPFound
      # OK
    else
      response.error!
    end
  end  
end

extension :hudson => :parametrized_job do
  task :produce_hudson_configuration do
    opts = fetch(:parametrized_job_options)
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        properties {
          _("hudson.model.ParametersDefinitionProperty") {
            parameterDefinitions {
              _!("hudson.model.StringParameterDefinition") {
                name(opts[:name])
                description(opts[:description])
                defaultValue(opts[:default] || "")
              }
            }
          }
        }
      }
    }
  end
end

extension :hudson_scm => :subversion do
  task :produce_hudson_configuration do
    raise "can't use hudson subversion bridge unless subversion SCM is used or :subversion_repository variable is specified" unless exists?(:subversion_repository) || fetch(:scm).type == :subversion
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        scm(:class => "hudson.scm.SubversionSCM") {
          locations {
            _("hudson.scm.SubversionSCM_-ModuleLocation") {
              remote(fetch(:subversion_repository, nil) || fetch(:repository))
              local("")
            }
          }
          useUpdate(fetch(:hudson_svn_use_update, false))
        }
      }
    }
  end
end

extension :hudson_scm => :none do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        scm(:class => "hudson.scm.NullSCM")
      }
    }
  end
end

extension :hudson_trigger => :scm_poll do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    hudson_job {
      project {
        triggers(:class => "vector") {
          _("hudson.triggers.SCMTrigger") {
            spec(fetch(:scm_poll_pattern))
          }
        }
      }
    }
  end
end

extension :hudson_trigger => :timer do
task :produce_hudson_configuration do
    execute_previous if previous_defined?
    hudson_job {
      project {
        triggers(:class => "vector") {
          _("hudson.triggers.TimerTrigger") {
            spec(fetch(:hudson_timer_trigger_pattern))
          }
        }
      }
    }
  end  
end

extension :hudson_builder => :shell do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        builders {
          _("hudson.tasks.Shell") {
            command(fetch(:hudson_build_shell_command))
          }
        }
      }
    }
  end
end

extension :hudson_builder => :seleniumhq do
  task :produce_hudson_configuration do
    opts = fetch(:hudson_seleniumhq_builder_options)
    execute_previous if previous_defined?

    hudson_job {
      project {
        builders {
          _("hudson.plugins.seleniumhq.SeleniumhqBuilder") {
            browser(opts[:browser] || "*firefox")
            startURL(opts.fetch(:start_url))
            suiteFile(opts.fetch(:suite_url))
            resultFile(opts[:result_file] || "selenium_report.html")
            other(opts[:other] || "")
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :seleniumhq
  end
end

extension :hudson_publisher => :build_trigger do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.tasks.BuildTrigger") {
            childProjects(fetch(:hudson_build_trigger_child_projects, []).join(","))
            threshold {
              name("SUCCESS")
              ordinal(0)
              color("BLUE")
            }
          }
        }
      }
    }
  end
end
  
extension :hudson_publisher => :parametrized_build_trigger do
  task :produce_hudson_configuration do
    opts = fetch(:hudson_parametrized_build_options)
    execute_previous if previous_defined?

    hudson_job {
      project {
        publishers {
          _("hudson.plugins.parameterizedtrigger.BuildTrigger") {
            configs {
              _!("hudson.plugins.parameterizedtrigger.PredefinedPropertiesBuildTriggerConfig") {
                projectsValue(opts[:projects] || "")
                properties(opts[:properties] || "")
                condition({ :class => "hudson.plugins.parameterizedtrigger.ResultCondition" }, (opts[:condition] || "SUCCESS").upcase)
                includeCurrentParameters(opts.fetch(:include_current_params, false))
              }
            }
          }
        }
      }
    }
  end

  task :fill_hudson_plugins do
    execute_previous if previous_defined?

    hudson_plugins << :parametrized_build_trigger
  end
end
  
extension :hudson_publisher => :junit_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?  

    hudson_job {
      project {
        publishers {
          _("hudson.tasks.junit.JUnitResultArchiver") {
            testResults(fetch(:hudson_junit_report_pattern, "**/junit_report.xml"))
          }
        }
      }
    }
  end
end

extension :hudson_publisher => :dry_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.dry.DryPublisher") {
            threshold {}
            newThreshold {}
            newFailureThreshold {}
            healthy {}
            unHealthy {}
            pluginName("[DRY] ")
            thresholdLimit("low")
            defaultEncoding {}
            pattern(fetch(:hudson_dry_report_pattern, "**/cpd_report.xml"))
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :dry
  end
end

extension :hudson_publisher => :findbugs_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.findbugs.FindBugsPublisher") {
            threshold {}
            newThreshold {}
            newFailureThreshold {}
            healthy {}
            unHealthy {}
            pluginName("[FINDBUGS] ")
            thresholdLimit("low")
            defaultEncoding {}
            pattern(fetch(:hudson_findbugs_report_pattern, "**/findbugs_report.xml"))            
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :findbugs
  end  
end

extension :hudson_publisher => :pmd_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.pmd.PmdPublisher") {
            threshold {}
            newThreshold {}
            newFailureThreshold {}
            healthy {}
            unHealthy {}
            pluginName("[PMD] ")
            thresholdLimit("low")
            defaultEncoding {}
            pattern(fetch(:hudson_pmd_report_pattern, "**/pmd_report.xml"))            
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :pmd
  end  
end

extension :hudson_publisher => :cobertura_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.cobertura.CoberturaPublisher") {
            coberturaReportFile(fetch(:hudson_cobertura_report_file, "**/cobertura_report.xml"))
            onlyStable(fetch(:hudson_cobertura_only_stable, true))
            
            %w(healthy_target unhealthy_target failing_target).each do |target|
              _(target) {
                targets {
                  entry {
                    _("hudson.plugins.cobertura.targets.CoverageMetric", "CONDITIONAL")
                    int(70)
                  }
                  entry {
                    _("hudson.plugins.cobertura.targets.CoverageMetric", "METHOD")
                    int(80)
                  }
                  entry {
                    _("hudson.plugins.cobertura.targets.CoverageMetric", "LINE")
                    int(80)
                  }
                }
              }
            end
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :cobertura
  end  
end

extension :hudson_publisher => :tasks_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.TasksPublisher") {
            threshold {}
            newThreshold {}
            newFailureThreshold {}
            healthy {}
            unHealthy {}
            pluginName("[TASKS] ")
            thresholdLimit("low")
            defaultEncoding {}
            high(fetch(:hudson_tasks_report_high_pattern, "FIXME"))
            normal(fetch(:hudson_tasks_report_normal_pattern, "TODO"))
            low(fetch(:hudson_tasks_report_low_pattern, ""))
            pattern(fetch(:hudson_tasks_report_pattern, "**/*.java"))            
          }
        }
      }
    }    
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :tasks
  end
end

extension :hudson_publisher => :status_monitor do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.statusmonitor.MonitorPublisher")
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :status_monitor
  end  
end

extension :hudson_publisher => :seleniumhq_report do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.seleniumhq.SeleniumhqPublisher") {
            testResults(fetch(:hudson_seleniumhq_report_pattern, "**/selenium_report.html"))
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :seleniumhq
  end
end

extension :hudson_publisher => :performance do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
 
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.performance.PerformancePublisher") {
            errorFailedThreshold(fetch(:hudson_performance_error_failed_threshold, 0))
            errorUnstableThreshold(fetch(:hudson_performance_error_unstable_threshold, 0))
            parsers {
              _("hudson.plugins.performance.JMeterParser") {
                glob("**/*.jtl")
              }
            }
          }
        }
      }
    } 
  end
     
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :performance
  end
end

extension :hudson_publisher => :jabber do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.jabber.im.transport.JabberPublisher") {
            targets(:class => "linked-list") {
              fetch(:hudson_jabber_default_recipients, []).each do |recipient|
                _("hudson.plugins.jabber.im.DefaultIMMessageTarget") {
                  value(recipient)
                }
              end
            }
            notificationStrategy({ :class => "hudson.plugins.jabber.NotificationStrategy"}, fetch(:hudson_jabber_notification_strategy, "ALL"))
            notifyOnBuildStart(fetch(:hudson_jabber_notify_on_build_start, false))
            notifySuspects(fetch(:hudson_jabber_notify_suspects, true))
            notifyFixers(fetch(:hudson_jabber_notify_fixers, true))
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :jabber
  end  
end

extension :hudson_publisher => :campfire do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?

    hudson_job {
      project {
        publishers {
          _("hudson.plugins.campfire.CampfireNotifier") {
            hudsonUrl(fetch(:hudson_campfire_hudson_url))
          }
        }
      }
    }
  end

  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :campfire
  end
end

extension :hudson_publisher => :emailext do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.emailext.ExtendedEmailPublisher") {
            recipientList(fetch(:hudson_emailext_recipient_list, ""))
            defaultSubject(fetch(:hudson_emailext_default_subject, "$DEFAULT_SUBJECT"))
            defaultContent(fetch(:hudson_emailext_default_content, "$DEFAULT_CONTENT"))
          }
        }
      }
    }
  end
  
  task :fill_hudson_plugins do
    execute_previous if previous_defined?
    
    hudson_plugins << :emailext
  end  
end

extension :hudson_publisher => :emailext_trigger do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
    
    hudson_job {
      project {
        publishers {
          _("hudson.plugins.emailext.ExtendedEmailPublisher") {
            # Possible triggers are: Failure, Fixed, Success and more (check the plugin UI in Hudson)
            _("hudson.plugins.emailext.plugins.trigger.#{fetch(:hudson_emailext_trigger).capitalize}") {
              email {
                options = fetch(:hudson_emailext_trigger_options, {})
                recipientList(options[:recipientList] || "")
                subject(options[:sibject] || "$PROJECT_DEFAULT_SUBJECT")
                body(options[:body] || "$PROJECT_DEFAULT_CONTENT")
                sendToDevelopers(options.fetch(:send_to_developers, true))
                includeCulprits(options.fetch(:include_culprits, false))
                sendToRecipientList(options.fetch(:send_to_recipient_list, false))
              }
            }
          }
        }
      }
    }
  end
end

extension :hudson_publisher => :email do
  task :produce_hudson_configuration do
    execute_previous if previous_defined?
  
    hudson_job {
      project {
        publishers {
          _("hudson.tasks.Mailer") {
            recipients(fetch(:hudson_mailer_recipients, ""))
            dontNotifyEveryUnstableBuild(fetch(:hudson_mailer_dont_notify_every_unstable_build, false))
            sendToIndividuals(fetch(:hudson_mailer_send_to_individuals, true))
          }
        }
      }
    }
  end
end

module ::Wondercap
module Extensions
module Hudson
  
  class XmlConfigurator
    attr :root
    
    def initialize(conf, root)
      @conf = conf
      @root = root
    end
    
    def fetch(*args, &block)
      @conf.fetch(*args, &block)
    end
    
    def exists?(name)
      @conf.exists(name)
    end
    
    def _!(name, attrs = nil, text = nil, &block)
      name = name + "!" unless name =~ /!$/
      _(name, attrs, text, &block)
    end
    
    def _(name, attrs = nil, text = nil, &block)
      if text.nil? && !attrs.nil? && !attrs.respond_to?(:keys)
        text = attrs
        attrs = nil
      end
      
      name_str = name.to_s
      nested_element = (name_str !~ /!$/ and @root.elements.find { |elem| elem.name == name_str }) || @root.add_element(name_str.chomp("!"))
      
      attrs.each { |k,v| nested_element.add_attribute(k.to_s, v.to_s) } unless attrs.nil?
      nested_element.add_text(text.to_s) unless text.nil?
      
      XmlConfigurator.new(@conf, nested_element).instance_eval(&block) if block
    end
    
    def method_missing(name, attrs = nil, text = nil, &block)
      _(name, attrs, text, &block)
    end
    
    def respond_to?(name, include_private = false)
      true
    end
  end
  
end
end
end
